all repos — caroster @ c45e87f1213a8a980ac0fc9fe510f124f5e1a225

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Tooltip from '@mui/material/Tooltip';
  3import IconButton from '@mui/material/IconButton';
  4import Button from '@mui/material/Button';
  5import Box from '@mui/material/Box';
  6import Link from '@mui/material/Link';
  7import Card from '@mui/material/Card';
  8import Container from '@mui/material/Container';
  9import TextField from '@mui/material/TextField';
 10import Typography from '@mui/material/Typography';
 11import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';
 12import EventIcon from '@mui/icons-material/Event';
 13import TuneIcon from '@mui/icons-material/Tune';
 14import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 15import {useTheme} from '@mui/material/styles';
 16import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 17import {PropsWithChildren, useState} from 'react';
 18import {useTranslation} from 'react-i18next';
 19import pageUtils from '../../../lib/pageUtils';
 20import ShareEvent from '../../../containers/ShareEvent';
 21import useEventStore from '../../../stores/useEventStore';
 22import useToastStore from '../../../stores/useToastStore';
 23import EventLayout, {TabComponent} from '../../../layouts/Event';
 24import {
 25  EventByUuidDocument,
 26  useUpdateEventMutation,
 27} from '../../../generated/graphql';
 28import PlaceInput from '../../../containers/PlaceInput';
 29
 30interface Props {
 31  eventUUID: string;
 32  announcement?: string;
 33}
 34
 35const Page = (props: PropsWithChildren<Props>) => {
 36  return <EventLayout {...props} Tab={DetailsTab} />;
 37};
 38
 39const DetailsTab: TabComponent = ({}) => {
 40  const {t} = useTranslation();
 41  const theme = useTheme();
 42  const [updateEvent] = useUpdateEventMutation();
 43  const addToast = useToastStore(s => s.addToast);
 44  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 45  const event = useEventStore(s => s.event);
 46  const [isEditing, setIsEditing] = useState(false);
 47
 48  if (!event) return null;
 49
 50  const onSave = async e => {
 51    try {
 52      const {uuid, ...data} = event;
 53      const {id, travels, waitingPassengers, __typename, ...input} = data;
 54      await updateEvent({
 55        variables: {
 56          uuid,
 57          eventUpdate: {
 58            ...input,
 59          },
 60        },
 61        refetchQueries: ['eventByUUID'],
 62      });
 63      setIsEditing(false);
 64    } catch (error) {
 65      console.error(error);
 66      addToast(t('event.errors.cant_update'));
 67    }
 68  };
 69
 70  const modifyButton = isEditing ? (
 71    <Tooltip
 72      title={t('event.details.save')}
 73      sx={{
 74        position: 'absolute',
 75        top: theme.spacing(2),
 76        right: theme.spacing(2),
 77      }}
 78    >
 79      <IconButton color="primary" onClick={onSave}>
 80        <CheckCircleOutlineIcon />
 81      </IconButton>
 82    </Tooltip>
 83  ) : (
 84    <Tooltip
 85      title={t('event.details.modify')}
 86      sx={{
 87        position: 'absolute',
 88        top: theme.spacing(2),
 89        right: theme.spacing(2),
 90      }}
 91    >
 92      <IconButton color="primary" onClick={() => setIsEditing(true)}>
 93        <TuneIcon />
 94      </IconButton>
 95    </Tooltip>
 96  );
 97
 98  return (
 99    <Box
100      sx={{
101        position: 'relative',
102      }}
103    >
104      <Container
105        sx={{
106          p: 4,
107          mt: 6,
108          mb: 11,
109          mx: 0,
110          [theme.breakpoints.down('md')]: {
111            p: 2,
112          },
113        }}
114      >
115        <Card
116          sx={{
117            position: 'relative',
118            maxWidth: '100%',
119            width: '350px',
120            p: 2,
121          }}
122        >
123          <Typography variant="h4" pb={2}>
124            {t('event.details')}
125          </Typography>
126          {modifyButton}
127          <Box pt={2} pr={1.5}>
128            <Typography variant="overline">{t('event.fields.name')}</Typography>
129            <Typography variant="body1">
130              {isEditing ? (
131                <TextField
132                  size="small"
133                  fullWidth
134                  value={event.name}
135                  onChange={e => setEventUpdate({name: e.target.value})}
136                  name="name"
137                  id="EditEventName"
138                />
139              ) : (
140                <Typography variant="body1" id="EventName">
141                  {event.name ?? t('event.fields.empty')}
142                </Typography>
143              )}
144            </Typography>
145          </Box>
146          <Box pt={2} pr={1.5}>
147            <Typography variant="overline">{t('event.fields.date')}</Typography>
148            {isEditing ? (
149              <Typography variant="body1">
150                <DatePicker
151                  slotProps={{
152                    textField: {
153                      size: 'small',
154                      id: `EditEventDate`,
155                      fullWidth: true,
156                      placeholder: t('event.fields.date_placeholder'),
157                    },
158                  }}
159                  format="DD/MM/YYYY"
160                  value={moment(event.date)}
161                  onChange={date =>
162                    setEventUpdate({
163                      date: !date ? null : moment(date).format('YYYY-MM-DD'),
164                    })
165                  }
166                />
167              </Typography>
168            ) : (
169              <Box position="relative">
170                <Typography variant="body1" id="EventDate">
171                  {event.date
172                    ? moment(event.date).format('DD/MM/YYYY')
173                    : t('event.fields.empty')}
174                </Typography>
175                <EventIcon
176                  color="action"
177                  sx={{
178                    position: 'absolute',
179                    right: theme.spacing(-0.5),
180                    top: 0,
181                  }}
182                />
183              </Box>
184            )}
185          </Box>
186          <Box pt={2} pr={1.5}>
187            <Typography variant="overline">
188              {t('event.fields.address')}
189            </Typography>
190            {isEditing ? (
191              <PlaceInput
192                place={event.address}
193                latitude={event.latitude}
194                longitude={event.longitude}
195                onSelect={({place, latitude, longitude}) =>
196                  setEventUpdate({
197                    address: place,
198                    latitude,
199                    longitude,
200                  })
201                }
202              />
203            ) : (
204              <Box position="relative">
205                <Typography variant="body1" id="EventAddress" sx={{pr: 3}}>
206                  {event.address ? (
207                    <Link
208                      target="_blank"
209                      rel="noreferrer"
210                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
211                        event.address
212                      )}`}
213                      onClick={e => e.preventDefault}
214                    >
215                      {event.address}
216                    </Link>
217                  ) : (
218                    t('event.fields.empty')
219                  )}
220                </Typography>
221                <PlaceOutlinedIcon
222                  color="action"
223                  sx={{
224                    position: 'absolute',
225                    right: theme.spacing(-0.5),
226                    top: 0,
227                  }}
228                />
229              </Box>
230            )}
231          </Box>
232          <Box pt={2} pr={1.5}>
233            <Typography variant="overline">
234              {t('event.fields.description')}
235            </Typography>
236            {isEditing ? (
237              <Typography variant="body1">
238                <TextField
239                  fullWidth
240                  multiline
241                  maxRows={4}
242                  inputProps={{maxLength: 250}}
243                  value={event.description || ''}
244                  onChange={e => setEventUpdate({description: e.target.value})}
245                  id={`EditEventDescription`}
246                  name="description"
247                />
248              </Typography>
249            ) : (
250              <Typography variant="body1" id="EventDescription" sx={{pr: 3}}>
251                {event.description ?? t('event.fields.empty')}
252              </Typography>
253            )}
254          </Box>
255          {!isEditing && (
256            <ShareEvent
257              title={`Caroster ${event.name}`}
258              sx={{width: '100%', mt: 2}}
259            />
260          )}
261        </Card>
262      </Container>
263    </Box>
264  );
265};
266
267export const getServerSideProps = pageUtils.getServerSideProps(
268  async (context, apolloClient) => {
269    const {uuid} = context.query;
270    const {host = ''} = context.req.headers;
271    let event = null;
272
273    // Fetch event
274    try {
275      const {data} = await apolloClient.query({
276        query: EventByUuidDocument,
277        variables: {uuid},
278      });
279      event = data?.eventByUUID?.data;
280    } catch (error) {
281      return {
282        notFound: true,
283      };
284    }
285
286    return {
287      props: {
288        eventUUID: uuid,
289        metas: {
290          title: event?.attributes?.name || '',
291          url: `https://${host}${context.resolvedUrl}`,
292        },
293      },
294    };
295  }
296);
297export default Page;